package com.linkoog.devtools.maven.dependency_folding;

import com.intellij.lang.ASTNode;
import com.intellij.lang.folding.FoldingBuilderEx;
import com.intellij.lang.folding.FoldingDescriptor;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiReferenceService;
import com.intellij.psi.impl.source.xml.XmlTagImpl;
import com.intellij.psi.impl.source.xml.XmlTextImpl;
import com.intellij.psi.impl.source.xml.XmlTokenImpl;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.psi.xml.XmlTokenType;
import com.intellij.util.xml.DomManager;
import com.intellij.util.xml.impl.GenericDomValueReference;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.maven.dom.model.MavenDomProjectModel;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DependencyFoldsBuilder extends FoldingBuilderEx {

    public static final Key<Boolean> USER_DATA = Key.create("com.linkoog.devtools.maven.dependency_folding");

    @Override
    public FoldingDescriptor[] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) {
        PsiFile psiFile = root.getContainingFile();
        if ( !(psiFile instanceof XmlFile xmlFile) ) return new FoldingDescriptor[0];

        XmlTag rootTag = xmlFile.getRootTag();
        if (rootTag == null) return new FoldingDescriptor[0];

        List<FoldingDescriptor> descriptors = new ArrayList<>();
        List<XmlTag> dependencies =  getTagsByName(rootTag);
        for (XmlTag dependency : dependencies) {
            // mark this node. This is the only way I found to be able to identify it later :(
            dependency.putUserData(USER_DATA, true);

            // create the descriptor
            XmlTokenImpl[] tagEndTokens = getTokensByType(dependency, XmlTokenType.XML_TAG_END);
            TextRange range;
            if (tagEndTokens.length > 0){
                int startOffset = tagEndTokens[0].getTextRange().getStartOffset();
                int endOffset = tagEndTokens[tagEndTokens.length - 1].getTextRange().getStartOffset();
                range = new TextRange(startOffset, endOffset);
            } else {
                int startOffset = dependency.getTextRange().getStartOffset();
                int endOffset = dependency.getTextRange().getEndOffset();
                range = new TextRange(startOffset, endOffset);
            }

            descriptors.add(new FoldingDescriptor(dependency.getNode(), range));
        }

        return descriptors.toArray(new FoldingDescriptor[0]);
    }

    @Override
    public String getPlaceholderText(@NotNull ASTNode node) {
        if (! (node instanceof XmlTag xmlTag)) return null;

        String groupId = xmlTag.getSubTagText("groupId");
        String artifactId = xmlTag.getSubTagText("artifactId");
        String version = xmlTag.getSubTagText("version");
        String scope = xmlTag.getSubTagText("scope");

        StringBuilder placeholderText = new StringBuilder();
        if (scope == null){

        } else {
            placeholderText.append(" [").append(scope).append("] ");
        }

        if (groupId == null){
            placeholderText.append("no groupId ").append(": ");
        } else {
            placeholderText.append(groupId).append(": ");
        }

        if (artifactId == null){
            placeholderText.append("no artifactId ").append(": ");
        } else {
            placeholderText.append(artifactId).append(": ");
        }

        if (version != null){
            placeholderText.append(version);
        }

        return placeholderText.toString();
    }

    @Override
    public boolean isCollapsedByDefault(ASTNode node) {
        PsiElement psiElement = (PsiElement)node;
        SettingsState settingsState = SettingsState.getInstance(psiElement.getProject());
        return settingsState.isCollapsedByDefault();
    }



    /** find XmlTagImpl with a given name */
    private List<XmlTag> getTagsByName(XmlTag xmlTag) {
        List<XmlTag> result = new ArrayList<>();
        XmlTag dependencyManagement = xmlTag.findFirstSubTag("dependencyManagement");
        if (dependencyManagement != null){
            result.addAll(findDependency(dependencyManagement));
        }
        result.addAll(findDependency(xmlTag));
        return result;
    }

    private List<XmlTag> findDependency(XmlTag xmlTag){
        List<XmlTag> result = new ArrayList<>();

        XmlTag dependencies = xmlTag.findFirstSubTag("dependencies");
        if (dependencies != null){
            XmlTag[] dependencyXmlTags = dependencies.findSubTags("dependency");
            result.addAll(Arrays.asList(dependencyXmlTags));
        }
        return result;
    }


    /** find XmlTokenImpl with a given elementType */
    private XmlTokenImpl[] getTokensByType(PsiElement psiElement, IElementType elementType) {
        List<XmlTokenImpl> result = new ArrayList<>();
        XmlTokenImpl[] tokens = PsiTreeUtil.getChildrenOfType(psiElement, XmlTokenImpl.class);
        if (tokens != null) {
            for (XmlTokenImpl token : tokens) {
                if (token.getElementType() == elementType) {
                    result.add(token);
                }
            }
        }
        return result.toArray(new XmlTokenImpl[0]);
    }
}
